# encoding: utf-8


module SemillaGen
  
  #Contains description information for a single template item.
  # A class template can have more than one file in it, for example:
  # a class definition and the test case definition for the class.
  class TemplateItem
    attr_accessor :target, :name_pattern, :source
  end
  
  
  
  # Generates items (Projects or Classes) using the specified
  # template and name.
  # 
  # @param [itemtype] The  kind of item to create, ':class' or ':project'.
  # @param [template] The name of the template to use.
  # @param [name] The name of project or qualified class name to generate.
  def self.generate(itemtype, template, name)
    
    #find the template folder
    template_folder = SemillaGen::findTemplate(itemtype, template)
    
    raise "Template not found." if template_folder.nil?
    
    
    if itemtype == :class
      #When item is a class type
      
      #Read the info.semilla file
      files = SemillaGen::parseInfoFile File.join(template_folder, "info.semilla")
      SemillaGen::generateFiles(template_folder, files, name)
    
    elsif itemtype == :project
      #when item is a project type
      
      SemillaGen::generateProject(template_folder, name)
    end
  end



  # Read a info.semilla file. This file contains
  # a description of the files to create.
  # Each line has the following format:
  #    filename_pattern : template_file => target_path
  # 
  #    filename_pattern = How the generated file should be named.
  #    template_file = The file whose contents will be used to generate the new file.
  #                    Some keywords inside the text will be replaced
  #                    (@@PACKAGE@@, @@NAME@@)
  #    target_path = The folder where the file should be created.
  #
  # @param [path] The path to the info.semilla file to read.
  #
  # @return [Array of TemplateItem] The collection of TemplateItem parsed.
  def self.parseInfoFile(path)
    
    files = {}
    
    if File.exists? path
      File.open(path, 'r') do |f|
        while(line = f.gets)
          line.strip! #Remove white space
          
          next if line.start_with?("#") or line.empty? #Skip comments or blank lines
          
          #Match "FILENAME => PATH"
          line.match(/([@\w.\-]+)\s*:\s*([\w.\-]+\.tpl) => ([\w.\-\/]+)/) do |m|
             nameFormat, sourceFile, targetPath = m.captures
             
             item = SemillaGen::TemplateItem.new
             item.target = targetPath
             item.name_pattern = nameFormat
             item.source = sourceFile
             
             files[sourceFile] = item
          end
          
        end
      end
    end
    
    return files
    
  end
  
  
  
  # Creates a directory hierarchy for the given directory path if the directories
  # do not exist.
  #
  # @param [path] The path to create if it does not exist already.
  def self.recursive_mkdir(path)
    steps = path.split File::SEPARATOR
    
    original_dir = Dir.pwd
    
    steps.each do |s|
      #Make dir if it does not exist
      if !File.directory? s
        Dir.mkdir s
        #puts "mkdir #{s}"
      end 
      Dir.chdir s
    end
    
    Dir.chdir original_dir
    
  end



  # Generates files for a class.
  # @param [templateFolder] The folder where the template to be used is located.
  # @param [fileList] The collection of TemplateItems to be generated.
  # @param [qualifiedName] The qualified name of the class to be generated (includes namespaces)
  def self.generateFiles(templateFolder, fileList, qualifiedName)
    puts "Generating files for: #{qualifiedName}"
    
    fileList.map do |key, item|
      #puts "----------#{key}"
      
      #Check that the sourceFile exists
      source_full_path = File.absolute_path(File.join(templateFolder, item.source))
      if File.exists? source_full_path
        #Obtain namespaces
        namespaces = qualifiedName.split '.' #Separate all the qualified name components
        itemName = namespaces.last #the last component is the class name
        namespaces = namespaces.take(namespaces.length-1) #The rest is the namespace (as array)
        namespace_text = namespaces.join('.') #the namespace in ActionScript notation
        
        #Append to the target path, the namespaces as folders
        final_path = namespaces.unshift(item.target).join(File::SEPARATOR)
        #Make sure the final_path directories exist
        self.recursive_mkdir final_path
        
        #Generate the new name
        fileName = item.name_pattern.sub('@@', itemName)
        
        #Read the source file
        source_text = IO.read source_full_path
        
        #Define the keywords to be replaced in the template
        keywords = {}
        keywords['@@PACKAGE@@'] = namespace_text
        keywords['@@NAME@@'] = itemName
        #Replace the keywords in the source text
        keywords.each do |k,v|
          source_text.gsub! k, v #use gsub to teplace all ocurrences
        end
        #Write out the file
        outpath = File.join(final_path, fileName)
        puts "    => #{outpath}"
        File.open(outpath, 'w') {|w| w << source_text}
                
      else #The source file is not found but it was specified in the info.semilla file.
        raise "Template file is missing: [#{item.source}]"
      end
      
    end
    
  end
  
  # Copies (while adjusting contents of files) files and directories recursively.
  #
  # @param [path] The path where the source files are read
  # @param [target_path] The path where the new files will be copied to
  # @param [options] Hash containing some settings for the file generation/adjustment
  def self.recursiveCopyDir(path, target_path, options)
    Dir.foreach path do |f|
      #Skip hidden files, unless in 'allowedFiles' list
      next if f.start_with?(".") and !options[:allowed_files].include?(f)
      
      #The absolute path for the template file/dir
      source_path = File.join path, f
      #The name of the file/dir to generate
      target_name = self.processText f, options
      #The absolute path of the file/dir to generate
      generate_path = File.join target_path, target_name
      
      if File.directory?(source_path)
        #Create the new dir
        Dir.mkdir generate_path
        puts "mkdir #{generate_path}"
        self.recursiveCopyDir(source_path, generate_path, options)
        
      else
        #Copy the file to the new destination
        
        # -> **Only process files whose extension is not in options[:dont_process_contents]
        should_process = options[:dont_process_contents].index{|x| source_path.end_with? x}.nil?
        if should_process
          # -> Read file contents
          fileContents = IO.read source_path
          # -> Adjust project name in content
          fileContents = self.processText fileContents, options
          # -> Write file to destination path
          puts "write #{generate_path} [E]" #The [E] at the end means that the file's contents were editted (ex: Project name replaced).
          File.open(generate_path, 'w') {|w| w << fileContents}
        else
          #just copy the file as is
          puts "write #{generate_path}"
          `cp "#{source_path}" "#{generate_path}"`
        end
        
        
      end
    end
  end
  
  
  # Replaces the DEFAULT_PROJECTNAME in the string to the one specified in options
  #
  # @param [text] The that will we processed
  # @param [options] Hash array with the values to be used while processing the text
  # @return [String]
  def self.processText(text, options)
    return text.gsub(SemillaGen::DEFAULT_PROJECTNAME, options[:project_name]) 
  end
  
  # Generates a project
  #
  # @param [templateFolder] The folder containing the project template to use
  # @param [name] The name of the new project
  def self.generateProject(templateFolder, name)
    puts "Generating project [#{name}]"
    
    #Create directory
    Dir.mkdir name
    
    options = {}
    options[:project_name] = name
    options[:allowed_files] = %w{.gitignore} 
    options[:dont_process_contents] = %w{.swc .swf .jar .exe .bin}
    
    target_path = File.join(Dir.pwd, name)
    
    self.recursiveCopyDir(templateFolder, target_path, options)
      
  end
  
  
  
end

